Skip to content

TUI warping indicator#13442

Merged
harryalbert merged 7 commits into
masterfrom
harry/code-1816-add-warping-indicator
Jul 8, 2026
Merged

TUI warping indicator#13442
harryalbert merged 7 commits into
masterfrom
harry/code-1816-add-warping-indicator

Conversation

@harryalbert

@harryalbert harryalbert commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Description

The TUI should show a ⋮ Warping (Ns) indicator while the selected conversation is in progress (Figma): a rotating spinner, a shimmering "Warping" label, and a ticking elapsed counter, rendered between the transcript and the input box. We already had the shimmer math for free (the GUI's ShimmeringTextElement), but the TUI had no animation machinery at all — redraws are purely invalidation-driven — so the bulk of this PR is building the native repaint-scheduling mechanism (mirroring the GUI's PaintContext::repaint_after) that the indicator then sits on top of. Animations run as paint-only redraws over the cached element tree, so nothing re-renders views or relayouts the transcript, and the TUI stays fully idle when nothing animates.

The most important files to look at are:

  • crates/warpui_core/src/elements/tui/mod.rs — the new TuiPaintContext with repaint_after + earliest-deadline-wins coalescing. This split (render/cursor now take TuiPaintContext instead of TuiLayoutContext) is what causes the mechanical signature churn across every TUI element.
  • crates/warpui_core/src/runtime/mod.rs — the driver side: each draw surfaces the frame's earliest requested repaint deadline and schedules a single one-shot timer for it, self-sustaining while anything animates.
  • crates/warpui_core/src/elements/tui/animated.rsTuiAnimated, the reusable animation element: a repaint cadence plus a closure that rebuilds the current frame each pass (drives the spinner and counter).
  • crates/warpui_core/src/elements/tui/shimmering_text.rs + crates/warpui_core/src/elements/shimmer_math.rs — the TUI shimmering text and the band math extracted from the GUI element so both backends share it.
  • crates/warp_tui/src/warping_indicator.rs — the indicator row itself: the spinner's keyframe choreography, the shimmer label, and the elapsed counter, all phase-locked to one AnimationClock.
  • crates/warp_tui/src/terminal_session_view.rs — visibility wiring: renders the row while the selected conversation is in progress, subscribed to history/selection events.

Testing

  • I have manually tested my changes locally with ./script/run

Screenshots / Videos

https://www.loom.com/share/60f0cc8358c3431f8233ba95380292fe

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

harryalbert commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@harryalbert harryalbert marked this pull request as ready for review July 7, 2026 19:59
@harryalbert harryalbert requested a review from kevinyang372 July 7, 2026 19:59
@oz-for-oss

oz-for-oss Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@harryalbert

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR adds the TUI warping indicator and the supporting repaint scheduling/animation plumbing across warp_tui and warpui_core, including shimmer math extraction and tests for the new TUI animation behavior.

Concerns

  • No blocking correctness, security, error-handling, performance, or spec-alignment concerns found in the annotated diff.
  • No approved or repository spec context was available for this PR, so there was no external spec drift to flag.
  • The PR description includes a Loom link, satisfying the repository's visual-evidence requirement for this user-facing TUI change.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@harryalbert harryalbert changed the title add tech spec TUI warping indicator Jul 7, 2026
@harryalbert harryalbert force-pushed the harry/code-1805-shell-command-execution branch from 37bafb8 to cab95a5 Compare July 7, 2026 20:17
@harryalbert harryalbert force-pushed the harry/code-1816-add-warping-indicator branch 2 times, most recently from 21cc85f to e516967 Compare July 8, 2026 17:49
@harryalbert harryalbert force-pushed the harry/code-1805-shell-command-execution branch from 9656467 to 2ad9ea3 Compare July 8, 2026 18:09
@harryalbert harryalbert force-pushed the harry/code-1816-add-warping-indicator branch from e516967 to b28ed1e Compare July 8, 2026 18:09
@harryalbert harryalbert force-pushed the harry/code-1805-shell-command-execution branch from 2ad9ea3 to 4f8db0a Compare July 8, 2026 18:24
@harryalbert harryalbert force-pushed the harry/code-1816-add-warping-indicator branch 2 times, most recently from c2820d9 to a56cce3 Compare July 8, 2026 18:46
Base automatically changed from harry/code-1805-shell-command-execution to master July 8, 2026 19:11
@harryalbert harryalbert force-pushed the harry/code-1816-add-warping-indicator branch from a56cce3 to da5c961 Compare July 8, 2026 19:12

@kevinyang372 kevinyang372 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice -- had one question

// but the caller drives the presenter standalone without the
// runtime's invalidate() step).
// 3. Direct render fallback for callers that skip invalidate().
// 2. Cached last_element — ONLY when invalidate() ran this frame, so

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a genuine behavior change yeah? This means if a view doesn't have animation AND no view was changed, we used to not repaint and reuse the last element tree. Now we are going to redraw for that case

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anyway we could capture if this redraw is triggered by an animation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a real behavior change, but I think in the opposite direction from what you're saying. The old condition was just a bad proxy for what we actually wanted to check.

Before, step 2 reused last_element when rendered_views was non-empty. That was meant to mean "invalidate ran, so the cache is trustworthy", but it's the wrong proxy: rendered_views holds cached child-view elements, and step 1 just popped the root out. So:

  • If the root has no child views, the map is empty at that point even though invalidate ran. Step 2 gets skipped and we fall to step 3, a full fresh render of the root view on every draw, pointlessly.
  • If the root has child views, the map stays non-empty across frames (cached child elements get re-inserted after every use), so the check couldn't actually detect "invalidate didn't run", which is the case its own comment said it was guarding. In practice every caller pairs invalidate + present so that path is unreachable, but the proxy was measuring map state, not the thing we cared about.

The new flag checks the intended condition directly: reuse the cache iff invalidate() ran this frame.

The reason this came up while building animation was that an animation repaint is a timer-triggered draw where nothing changed. The draw still runs invalidate(), but with an empty set, so nothing gets re-rendered and the root is never in rendered_views. Step 2 is the only thing that lets us repaint the cached tree; under the old emptiness check, a childless root would have re-run its whole render() at the spinner's cadence, which is bad lol.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and per the animation redraw capture, that should already be encoded. An animation-triggered draw is exactly a draw whose invalidation set is empty, so invalidate() re-renders nothing and present() reuses the cached tree. That reuse is the animation fast path. We could thread an explicit "draw reason" from the runtime, but it would just duplicate what the invalidation set already tells us.

@harryalbert harryalbert force-pushed the harry/code-1816-add-warping-indicator branch from 3b42bf4 to a984806 Compare July 8, 2026 21:22
@harryalbert harryalbert merged commit fa0d6fc into master Jul 8, 2026
26 checks passed
@harryalbert harryalbert deleted the harry/code-1816-add-warping-indicator branch July 8, 2026 21:44
ianhodge added a commit that referenced this pull request Jul 8, 2026
Rebased squash of the CODE-1831 branch onto latest master (which now
includes the TUI warping indicator, #13442). Granular history including
the preserved OSC 22 pointer implementation (348484d, referenced by
CODE-1837) lives on the backup branch ian/code-1831-pre-rebase.

The footer shows the selected conversation's GUI-consistent credits
total after model + cwd, hidden until the first usage report; clicking
toggles to the accumulated provider dollar cost and back; hover
brightens. Credits come from the server's cumulative usage metadata
formatted with the GUI's format_credits (exported through tui_export);
cost sums the per-request StreamFinished usage rows. Includes the
CODE-1828 tech spec.

Co-Authored-By: Oz <oz-agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants